//
//  CZSQLiteManager.swift
//  001-数据库
//
//  Created by 名品导购网MPLife.com on 2017/12/19.
//  Copyright © 2017年 sweet. All rights reserved.
//

import Foundation
import FMDB
//最大的数据库缓存时间 以 s 为单位
private let maxDBCacheTime:TimeInterval = -60

/// SQLite管理
/*
 数据库本质上是保存在沙盒中的一个文件  首先需要创建并且打开数据库
 FMDB-队列
 创建数据表
 增删改查
 数据库开发 程序代码都是一样的区别在于SQL不同
 开发数据库功能的时候 要先测试SQL的正确性
 
 */
class CZSQLiteManager {
    //全局的数据库工具单例
    static let shared = CZSQLiteManager()
    //创建数据库队列

    let queue:FMDatabaseQueue

    //构造函数
   private init() {
    //数据库的全路径
    let dbName = "status.db"
    var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    
    //拼接路径
    path = (path as NSString).appendingPathComponent(dbName)
    //创建队列 同时创建或打开数据库
    queue = FMDatabaseQueue(path: path)
    //打开数据库
    createTable()
    //注册通知 监听程序进入后台清理数据缓存
    NotificationCenter.default.addObserver(self, selector: #selector(cleaeDBCache), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }
    //SQLite的数据不断的增加数据 数据库文件的大小会不断的增加
    //但是如果删除了数据 数据库的大小不会变小
    //如果要变小，
    //1.备份将数据库文件 复制一个新的副本 status.db.old
    //新建一个空的数据库文件
    //3.自己编写SQL 从old中将所有的数据读出  写入新的数据库
    @objc private func cleaeDBCache(){
        let dateString = Date.cz_dateString(delta: maxDBCacheTime)
//准备SQL
        let sql = "DELETE FROM T_Status WHERE createTime < ?;"
        //执行SQL
        queue.inDatabase { (db) in
            if db?.executeUpdate(sql, withArgumentsIn: [dateString]) == true {
            }
        }
        
    }
    //移除通知
    deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
    }
}

//微博数据操作
extension CZSQLiteManager{
    
    /// 从数据库加载微博数据数组
    ///
    /// - Parameters:
    ///   - userId: 当前登录的用户账号
    ///   - since_id: 返回比since_id大的微博
    ///   - max_id: 返回比max_id小的微博
    /// - Returns: 返回微博字典的数组 将数据库中status对应的二进制数据反序列化 生成字典
    func loadStatus(userId:String,since_id: Int64 = 0, max_id: Int64 = 0)->[[String:AnyObject]] {
        //1.准备SQL
        /*var sql = "SELECT statusId,userId, status FROM T_Status"
        sql += "WHERE userId = \(userId)"
        if since_id > 0 {
            sql += "AND statusId > \(since_id)"
        }else if max_id > 0{
            sql += "AND statusId < \(max_id)"
        }
        sql += "ORDER BY statusId DESC LIMT 20;"*/
        
        
        var sql = "SELECT statusId, userId, status FROM T_Status \n"
        sql += "WHERE userId = \(userId) \n"
        
        // 上拉／下拉，都是针对同一个 id 进行判断
        if since_id > 0 {
            sql += "AND statusId > \(since_id) \n"
        } else if max_id > 0 {
            sql += "AND statusId < \(max_id) \n"
        }
        
        sql += "ORDER BY statusId DESC LIMIT 20;"
        
//        print(sql)
        //2.执行SQL
        let array = execRecordSet(sql: sql)
        //3.遍历数组，将数组中的status反序列化
        var result = [[String:AnyObject]]()
        for dict in array {
            //反序列化
            guard let jsonData = dict["status"] as? Data, let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as?[String:AnyObject] else{
                continue
            }
           //追加到数组
            result.append(json ?? [:])
        }
        return result
    }
    /// 新增或则修改微博数据 微博数据在刷新的时候 可能会出现重叠
    /// - Parameters:
    ///   - userId: 当前登录用户的id
    ///   - array: 从网络获取字典数组
    func updateStatus(userId:String,array:[[String:AnyObject]]) {
      //1.准备SQL
        /*
         statusId当前要保存的微博代号
         userId当前登录用户Id
         status:完整微博字典的json二进制数据
         */
        let sql = "INSERT OR REPLACE INTO T_Status (statusId, userId, status) VALUES (?, ?, ?);"
     //2.执行SQL
        queue .inTransaction { (db, rollback) in
            //遍历数组 逐条插入微博数据
            //从字典获取微博代号
            for dict in array {
            guard let statusId = dict["idstr"] as? String,let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: []) else{
                    continue
                }
                //执行SQL
               if db?.executeUpdate(sql, withArgumentsIn: [statusId,userId,jsonData]) == false{
                    //需要回滚 fixme
                //Xcode自动语法转换，不会处理此处
                rollback?.pointee = true
                break
                }
            }
        }
    }
}
 extension CZSQLiteManager{
    /// 执行一个SQL 返回字典的数组
    ///
    /// - Parameter sql: sql：sql
    /// - Returns: 字典的数组
    func execRecordSet(sql:String) -> [[String:AnyObject]] {
        
        var result = [[String:AnyObject]]()
        
        
        //执行SQL 查询数组 不会修改数据 所以不需要开启事务
        //事务的目的 是为了保证数据的有效性 一但失败 回滚到初始状态
        queue.inDatabase { (db) in
            
            guard let rs = db?.executeQuery(sql, withArgumentsIn: []) else{
                return
            }
            
            //遍历结果集合
            while  rs.next() {
                //列数
                let  colCount = rs.columnCount()
                //遍历所有列
                for col in 0..<colCount{
                    guard let name = rs.columnName(for: col),let value = rs.object(forColumnIndex: col) else{
                        continue
                    }
                    result.append([name:value as AnyObject])
                }
                
            }
        }
        return result
    }
}
//创建数据表 以及其他私有方法
private extension CZSQLiteManager{
    //创建数据表
    func createTable() {
        //1.SQL
        guard let path = Bundle.main.path(forResource: "status.sql", ofType: nil) else {
            return
        }
        //带throw要弱try
        let sql = try? String(contentsOfFile: path)
        //2.执行SQL  FMDB的内部是串行队列  同步执行
        //可以保证同一时间 只有一个任务操作数据库 从而保证数据库的读写安全
        queue.inDatabase { (db) in
            if db?.executeStatements(sql) == true{
                //只有在创建表格的时候 使用执行多条语句 可以一次创建多个数据表 在执行增删改的时候 一定不要使用statements方法 否则有可能会被注入
                print("创表成功")
            }else{
                print("创表失败")
            }
        }
        print("over")
    }
}
































